﻿//-----------------------------------------------------------------------
// <copyright file="ExtractPackageStage.cs" author="Artak Mkrtchyan">
//     Creative Commons - GNU General Public License (http://creativecommons.org/licenses/GPL/2.0/
// </copyright>
// <author>Artak Mkrtchyan</author>
// <email>mkArtak@gmail.com</email>
// <date>20/03/2013</date>

using ConsoleHoster.Common.Utilities;
using ConsoleHoster.Common.Utilities.IO;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Reflection;

namespace AppUpdateInstaller.Model.InstallationStages
{
	internal sealed class ExtractPackageStage : InstallationStageBase
	{
		private readonly string archiveFilePath;
		private readonly string updateFolder;
		private readonly string binariesFolder;
		private readonly Action<string, string> extractZipArchive;

		public ExtractPackageStage(string argArchiveLocation, string argBinariesFolderPath, IProcessManager argProcessManager, IFileSystemContentProvider argFileSystemContentProvider, Action argShutdownLogic)
			: this(argArchiveLocation, argBinariesFolderPath, argProcessManager, argFileSystemContentProvider, argShutdownLogic, (zip, folder) => ZipFile.ExtractToDirectory(zip, folder))
		{
		}

		public ExtractPackageStage(string argArchiveLocation, string argBinariesFolderPath, IProcessManager argProcessManager, IFileSystemContentProvider argFileSystemContentProvider, Action argShutdownLogic, Action<string, string> argExtractArchive)
			: base(argProcessManager, argFileSystemContentProvider, argShutdownLogic)
		{
			if (!this.FileSystemContentProvider.FileExists(argArchiveLocation))
			{
				throw new FileNotFoundException("The specified archive file is not found");
			}

			if (!this.FileSystemContentProvider.DirectoryExists(argBinariesFolderPath))
			{
				throw new DirectoryNotFoundException("Invalid binaries folder specified");
			}

			if (argExtractArchive == null)
			{
				throw new ArgumentNullException("argExtractArchive");
			}

			this.archiveFilePath = argArchiveLocation;
			this.updateFolder = Path.Combine(this.FileSystemContentProvider.GetFileDirectory(this.archiveFilePath), this.FileSystemContentProvider.GetRandomFolderName());
			this.binariesFolder = argBinariesFolderPath;
			this.extractZipArchive = argExtractArchive;
		}

		public override void Run()
		{
			this.NotifyProgressChange(new InstallationStageProgressEventArgs { Status = "Extracting update package..." });
			this.extractZipArchive(this.archiveFilePath, this.updateFolder);
		}

		public override void FinalizeStage()
		{
			this.NotifyProgressChange(new InstallationStageProgressEventArgs { Status = "Switching to the updated installer..." });

			string tmpPath = Path.Combine(this.updateFolder, Assembly.GetExecutingAssembly().GetName().Name + ".exe");

			using (Process tmpProcess = this.ProcessManager.StartProcess(tmpPath, StringUtilities.Format("{0} {1} {2}", InstallerMode.UpdateBinaries.ToString(), this.binariesFolder, this.updateFolder)))
			{
				tmpProcess.WaitForInputIdle();
			}

			if (this.ShutdownAction != null)
			{
				this.ShutdownAction();
			}
		}

		public override bool HasFinalizerStage
		{
			get
			{
				return true;
			}
		}
	}
}
